#include <iostream>
#include <cmath>
#include "Function.h"
#include "InitInput.h"

class EquationSolver
{
    public:
        double eps;
        double max;
    
        EquationSolver(double e, double m): eps(e), max(m){};
        ~EquationSolver(){};
        virtual double solve(const InitInput &condition)=0;
};

class Bisection :public EquationSolver
{
    public:
        double del;
    
        Bisection(double e, double d, double m): EquationSolver(e, m), del(d){};
        ~Bisection(){};
        double solve(const InitInput &condition)override;
};
double Bisection::solve(const InitInput &condition){
    if(condition.f.get_value(condition.a)*condition.f.get_value(condition.b) > 0){
        std::cout << "Error: Interval[" << condition.a << "," << condition.b << "] cannot determine a root!" << std::endl;
        return 0;
    }
    double h = condition.b-condition.a;
    double u = condition.f.get_value(condition.a);
    double c,w;
    double a = condition.a;
    for (int k = 0; k <= max; k++){
        h = h/2;
        c = a + h;
        if (fabs(h) < del || k == max)
            break;
        w = condition.f.get_value(c);
        if (fabs(w) < eps)
            break;
        else if (w * u > 0){
            a = c;
            u = condition.f.get_value(a);
        }
    }
    return c;
}

class Newton :public EquationSolver
{
    public:

        Newton(double e, double m): EquationSolver(e, m){}
        ~Newton(){};
        double solve(const InitInput &condition)override;
};
double Newton::solve(const InitInput &condition){
    double x = condition.x0;
    double u;
    for (int k = 0; k <= max; k++){
        u = condition.f.get_value(x);
        if (fabs(x) < eps)
            break;
        x = x-u/condition.f.get_derivation(x);
    }
    return x;
}

class Secant :public EquationSolver
{
    public:
        double del;

        Secant(double e, double d, double m): EquationSolver(e, m), del(d){}
        ~Secant(){};
        double solve(const InitInput &condition)override;
};
double Secant::solve(const InitInput &condition){
    double x = condition.x1;
    double y = condition.x0;
    double u = condition.f.get_value(x);
    double v = condition.f.get_value(y);
    double s;
    for (int k = 2; k <= max; k++){
        s = (x-y)/(u-v);
        y = x;
        v = u;
        x = x-u*s;
        if (fabs(x-y) < del)
            break;
        u = condition.f.get_value(x);
        if (fabs(u) < eps)
            break;
    }
    return x;
}